package com.bigknow.appstore.web.base.controller;

import com.bigknow.appstore.web.base.service.IConsoleService;
import com.bigknow.appstore.web.console.app.Application;
import com.bigknow.appstore.web.console.app.OauthCode;
import com.bigknow.appstore.web.console.service.impl.ApplicationService;
import com.bigknow.appstore.web.framework.controller.BaseController;
import com.bigknow.appstore.web.framework.security.CaptchaFormAuthenticationFilter;
import com.bigknow.appstore.web.framework.security.SecurityRealm;
import com.bigknow.appstore.web.framework.security.exception.AuthenticationNotUseException;
import com.bigknow.appstore.web.framework.util.AuthenticateUserUtil;
import com.bigknow.appstore.web.framework.util.CaptchaUtils;
import com.bigknow.appstore.web.framework.util.GlobalConstant;
import com.bigknow.appstore.web.base.entity.Role;
import com.bigknow.appstore.web.base.entity.User;
import com.bigknow.appstore.web.base.service.impl.RoleService;
import com.bigknow.appstore.web.framework.security.CaptchaValidationException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.mongodb.morphia.Datastore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * Created by 李科风 on 2016-06-07.
 */
@Controller
@RequestMapping(value = "/")
public class LoginController extends BaseController {

    @Autowired
    private IConsoleService consoleService;

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private Datastore ds;

    /**
     * 界面提交登陆调用的后台方法.
     * 授权部分在shiro框架中完成,见{@link SecurityRealm}<br>
     * 验证码验证在{@link CaptchaFormAuthenticationFilter}中.
     * 这里只要判断是否登陆就行了
     *
     * @param request
     * @param response
     * @param model    springMvc的model,在model中添加的对象会直接映射到request中并映射到freemarker中，
     *                 使其可以直接调用
     * @return controller注册的其他url
     */
    @RequestMapping(value = {"/login"})
    public String login(HttpServletRequest request, HttpSession session, HttpServletResponse response, Model model, String target, String login_type, String client_id, String state, String redirect_uri) {
        clearError(request);//清除上一次请求的错误信息!

        if (!consoleService.hasInit()) {
            consoleService.initConsole();
        }
        Subject subject = SecurityUtils.getSubject();
        try {
            //登陆页面标识,用于界面上jquery等ajax框架判断页面类型,作为session超时等页面跳转用
            ((HttpServletResponse) response).setHeader("PageNameValue", "login");
            if ((subject.isAuthenticated() || subject.isRemembered()) && isRightUser(target)) {
                session.setAttribute("target", target);
                User user = (User) subject.getPrincipal();
                if (!StringUtils.isEmpty(login_type) && StringUtils.equals("app", login_type)) {
                    //如果是app oauth授权重定向访问，跳转至app页面
                    com.bigknow.appstore.web.console.app.OauthCode _code = ds.find(com.bigknow.appstore.web.console.app.OauthCode.class).filter("client_id", client_id).get();
                    String code;
                    if (_code == null) {
                        code = UUID.randomUUID().toString();
                        OauthCode oauthCode = new OauthCode();
                        oauthCode.setClient_id(client_id);
                        oauthCode.setCreate_time(System.currentTimeMillis());
                        oauthCode.setCode(code);
                        ds.save(oauthCode);
                    } else {
                        code = _code.getCode();
                    }
                    Application app = ds.find(Application.class).filter("ssoConfig.clientId", client_id).filter("userId", user.getId()).get();
                    if (app != null) {
                        response.sendRedirect(applicationService.getUrl(app.getId()) + "/" + app.getSsoConfig().getRedirectURL() + "?code=" + code + "&state=" + state + "&userId=" + user.getId());
                    }
                    return null;
                } else {
                    if (isConsumer()) {
                        return "redirect:app";
                    } else {
                        return "redirect:main";
                    }
                }
            } else {
                // shiro 配置的formAuthenticationFilter会将错误类型放入request 的shiroLoginFailure元素中,采用如下方式可以获取异常类型
                addErrorMessage((String) request.getAttribute(
                        FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME), model);
                subject.logout();
                return "redirect:re_login";
            }
        } catch (Exception e) {
            e.printStackTrace();
            subject.logout();
            return "redirect:re_login";
        }
    }

    private boolean isConsumer() {
        String[] roleArray = AuthenticateUserUtil.getUser().getRoleIds().split(",");
        for (String id : roleArray) {
            Role role = roleService.get(id);
            switch (role.getCode()) {
                case "consumer": {
                    return true;
                }
            }
        }
        return false;
    }

    @RequestMapping(value = {"/re_login"})
    @ResponseBody
    public String reLogin() {
        return "<script>top.location.href='/login_view'</script>";
    }

    @RequestMapping(value = {"/login_view"})
    public String loginView() {
        return "/user_login";
    }

    /**
     * 判断管理员是否有与target相称的角色
     * 只要角色中包含相应的角色就返回true
     *
     * @param target
     * @return
     */
    private boolean isRightUser(String target) {
        String[] roleArray = AuthenticateUserUtil.getUser().getRoleIds().split(",");
        boolean returnValue = false;
        for (String id : roleArray) {
            Role role = roleService.get(id);
            switch (role.getCode()) {
                case "appManager":
                case "consumer":
                case "admin": {
                    returnValue = true;
                    break;
                }
            }
            if (returnValue) return returnValue;
        }
        return returnValue;
    }

    /**
     * 不同用户的主页
     *
     * @param model
     * @param session
     * @return
     */
    @RequestMapping(value = "/main")
    public String index(Model model, HttpSession session) {
        addUserInfoToPage(model);
        return "/manager_index";
    }

    /**
     * 主页,如果没有登陆或者没有remember就弹到登陆页面
     *
     * @param request
     * @param model
     * @return
     */
    @RequestMapping(value = "/")
    public String index(HttpServletRequest request, Model model) {
        clearError(request);
        //如果已经登录,就不能再弹出登录界面了
        if (SecurityUtils.getSubject().isAuthenticated() || SecurityUtils.getSubject().isRemembered()) {
            addUserInfoToPage(model);//将user信息加入到model，避免记住当没登陆用户没法显示页面信息。
            return "redirect:main";
        }
        return "login";
    }

    /**
     * 当登陆后出现超时，或者remembered后需要重新登陆时，这个时候页面处在main区域
     * navigationBar.js通过jquery ajaxSetup将整个页面定位到此，跳过页面认证判断，
     * 避免造成认证问题。
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/goToLogin")
    public String goToLogin(HttpServletRequest request) {
        clearError(request);
        return "login";
    }

    @RequestMapping("/error")
    public String error() {
        return "error";
    }

    /**
     * 退出登陆
     *
     * @param request
     * @return
     */
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request) {
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }

    /**
     * 生成验证码
     *
     * @return
     * @throws IOException
     */
    @RequestMapping("/getCaptcha")
    public void getCaptcha(HttpSession session, HttpServletResponse response) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_JPEG);

        CaptchaUtils tool = new CaptchaUtils();
        StringBuffer code = new StringBuffer();
        BufferedImage image = tool.genRandomCodeImage(code);
        session.removeAttribute(GlobalConstant.DEFAULT_CAPTCHA_PARAM);
        session.setAttribute(GlobalConstant.DEFAULT_CAPTCHA_PARAM, code.toString());

        // 将内存中的图片通过流动形式输出到客户端
        ImageIO.write(image, "JPEG", response.getOutputStream());
        return;
    }

    private void clearError(HttpServletRequest request) {
        request.setAttribute("error", null);
    }

    private void addErrorMessage(String exceptionType, Model model) {
        logger.info("error: " + exceptionType);
        if (AuthenticationException.class.getName().equals(exceptionType)) {
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_AUTHENTICATION_EXCEPTION));
        } else if (ExcessiveAttemptsException.class.getName().equals(exceptionType)) {
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_EXCESSIVE_ATTEMPTS_EXCEPTION));
        } else if (exceptionType == null) {
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_SESSION_TIMEOUT_EXCEPTION));
        } else if (CaptchaValidationException.class.getName().equals(exceptionType)) {
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_CAPTCHA_VALIDATION_EXCEPTION));
        } else if (AuthenticationNotUseException.class.getName().equals(exceptionType)) {
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_AUTHENTICATION_EXCEPTION_NOT_USE));
        } else {
            //没有匹配的异常
            model.addAttribute("error", springUtils.getMessage(GlobalConstant.KEY_UNKNOWN_AUTHENTICATION_EXCEPTION));
        }
    }

    private void addUserInfoToPage(Model model) {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("isAdmin", ArrayUtils.contains(user.getRoleIds().split(","), "1"));
        model.addAttribute("user", user);
    }

}
